#include "vcl_stl.h"

#include "bkbase.h"
#include "h_base.h"
#include "h_xml.h"
using namespace h_base;

#include "simbat.h"
using namespace simbat;

#include "bkipc.h"

struct HClusterNodeInfo
{
   int Id;
   HString Addr;
   int Port;

   HClusterNodeInfo()
      : Id(-1), Port(-1)
   {}
};

const int COMM_TIME_OUT = 30000;

static const char *SimbatBase = NULL;
static char ErrMsg[8192 + 1];

static HString SyslogDir;
static HSyslog Syslog;
static int LocalPort;
static HString ShrDir;
static HString LogDir;

static bool ClusterFlag = false;                    // 是否集群
static HNodeRole MyRole = nrMaster;                 // 本服务器角色（MASTER/SLAVE/BACKUP）
static int MyId = -1;                               // 本服务器ID（从0开始）
static vector<HClusterNodeInfo> ClusterNodeVec;     // 集群配置信息
static bool HotbackEnableFlag = false;              // 是否启动热备
static bool HotbackPausedFlag = false;              // 是否暂停热备
static HString HotbackAddr;                         // 热备机IP地址
static int HotbackPort = -1;                        // 热备机端口
static map<HString, HBackupInfo> BackupMap;         // 若干备份集群
static HBackupObjType ObjType = boShr;
static int MasterId = 0;

static void LoadFromCfg(HXmlTree &Cfg)
{
   SyslogDir = EnvEvaluate(Cfg.GetValue("SYSLOG_DIR", "$SIMBAT_BASE/etc"));
   Syslog.Setup(SyslogDir, Cfg.GetValueUpper("SYSLOG_LEVEL", "INFO"));

   if ( !GetMyId(SimbatBase, MyId, MyRole, ErrMsg) )
   {
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      return;
   }

   LocalPort = StrToInt(Cfg.GetValue("PORT").c_str());
   ShrDir = EnvEvaluate(Cfg.GetValue("SHR_DIR", "$SIMBAT_BASE/shr"));
   LogDir = EnvEvaluate(Cfg.GetValue("LOG_DIR", "$SIMBAT_BASE/log"));

   {
      vector<HString> StrVec;
      Cfg.GetValues("CLUSTER", StrVec);
      ClusterFlag = StrVec.size() > 1;
      if ( ClusterFlag )
      {
         ClusterNodeVec.clear();
         for ( int i = 0; i < StrVec.size(); i++ )
         {
            HString &AddrStr = StrVec[i];
            vector<HString> AddrPort;
            SplitString(AddrStr.c_str(), ':', AddrPort, true);
            if ( AddrPort.size() != 2 && AddrPort.size() != 3 )
            {
               snprintf(ErrMsg, 8192, "Invalid cluster address(%s)", AddrStr.c_str());
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               return;
            }
            HClusterNodeInfo Node;
            Node.Id = i;
            Node.Addr = AddrPort[0];
            Node.Port = StrToInt(AddrPort[1].c_str());
            if ( Node.Port <= 2048 )
            {
               snprintf(ErrMsg, 8192, "Port %s too small, must be greater than 2048", AddrStr.c_str());
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               return;
            }
            ClusterNodeVec.push_back(Node);
         }
      }
      else
      {
         HClusterNodeInfo Node;
         Node.Id = 0;
         Node.Addr = "127.0.0.1";
         Node.Port = LocalPort;
         ClusterNodeVec.push_back(Node);
         MyId = 0;
         MyRole = nrMaster;
      }
   }

   {
      HotbackEnableFlag = ( Cfg.GetValueUpper("HOTBACK_ENABLE", "FALSE") == "TRUE" );
      HotbackPausedFlag = ( Cfg.GetValueUpper("HOTBACK_PAUSED", "FALSE") == "TRUE" );
      HString AddrStr = Cfg.GetValue("HOTBACK_ADDR");
      if ( AddrStr == "" )
      {
         HotbackEnableFlag = false;
         HotbackAddr = "";
         HotbackPort = -1;
      }
      else
      {
         vector<HString> AddrPort;
         SplitString(AddrStr.c_str(), ':', AddrPort, true);
         if ( AddrPort.size() == 2 )
         {
            HotbackAddr = AddrPort[0];
            HotbackPort = StrToInt(AddrPort[1].c_str());
         }
         else
         {
            HotbackEnableFlag = false;
            HotbackAddr = "";
            HotbackPort = -1;
         }
      }
   }

   LoadBackupInfoFromCfg(Syslog, Cfg, BackupMap);
}

static bool LoadCfg(HString &ErrStr)
{
   char FileName[500 + 1];
   HXmlTree Cfg;

   {
      HFileLockRead Lock("$SIMBAT_BASE/tmp/config.lck");
      snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);
      if ( !Cfg.LoadFromFile(FileName) )
      {
         ErrStr = Cfg.GetErrorString();
         return false;
      }
   }

   LoadFromCfg(Cfg);
   return true;
}

void PrintUsage()
{
   fprintf(stderr, "Usage : [PATH]/simbat_backup_file [--type=obj_type] obj_name\n");
   fprintf(stderr, "   obj_type can be shr, log or other. Default is shr\n");
}

int main(int argc, char *argv[])
{
   if ( strchr(argv[0], '/') != NULL )
   {
      SetBaseByPath(argv[0]);
   }

   if ( argc == 1 )
   {
      PrintUsage();
      return 8;
   }

   SimbatBase = getenv("SIMBAT_BASE");

   if ( SimbatBase == NULL )
   {
      fprintf(stderr, "$SIMBAT_BASE not defined\n");
      return 8;
   }

   HString ErrStr;

   if ( !LoadCfg(ErrStr) )
   {
      BkPrintError("Load config.xml fail, %s", ErrStr.c_str());
      PrintUsage();
      return 8;
   }

   if ( MyRole != nrMaster && MyRole != nrSlave )
   {
      BkPrintError("This node is not master or slave");
      return 8;
   }

   int i;

   for ( i = 1; i < argc; i++ )
   {
      if ( memcmp(argv[i], "--type=", 7) == 0 )
      {
         if ( strcmp(argv[i] + 7, "shr") == 0 )
         {
            ObjType = boShr;
         }
         else if ( strcmp(argv[i] + 7, "log") == 0 )
         {
            ObjType = boJobLog;
         }
         else if ( strcmp(argv[i] + 7, "other") == 0 )
         {
            ObjType = boOther;
         }
         else
         {
            PrintUsage();
            return 8;
         }
         continue;
      }
      break;
   }

   if ( i + 1 != argc )
   {
      PrintUsage();
      return 8;
   }

   const char *ObjName = argv[i];
   char FileName[500 + 1];

   switch ( ObjType )
   {
   case boShr :
      snprintf(FileName, 500, "%s/%s", ShrDir.c_str(), ObjName);
      break;
   case boJobLog :
      snprintf(FileName, 500, "%s/%s", LogDir.c_str(), ObjName);
      break;
   case boOther :
      snprintf(FileName, 500, "%s/%s", SimbatBase, ObjName);
      break;
   }

   snprintf(ErrMsg, 8192, "BackupFile(ObjName = %s)", ObjName);
   Syslog.Log(__FILE__, __LINE__, llDebug, "ENTRY", ErrMsg);

   if ( MyRole == nrSlave && ObjType != boJobLog ) // 发给 master
   {
      if ( !SendBackupFile(Syslog, ShrDir, LogDir, SimbatBase,
               ClusterNodeVec[MasterId].Addr.c_str(), ClusterNodeVec[MasterId].Port,
               ObjType, ObjName, 0, FileExist(FileName) ? BkFileSize(FileName) : -1, "Y") )
      {
         BkPrintError("SendBackupFile fail");
         return 8;
      }
      return 0;
   }

   if ( HotbackEnableFlag && !HotbackPausedFlag )
   {
      if ( !SendBackupFile(Syslog, ShrDir, LogDir, SimbatBase, HotbackAddr.c_str(), HotbackPort,
              ObjType, ObjName, 0, FileExist(FileName) ? BkFileSize(FileName) : -1) )
      {
         BkPrintError("SendBackupFile fail");
         return 8;
      }
   }

   for ( map<HString, HBackupInfo>::iterator Iter = BackupMap.begin();
         Iter != BackupMap.end(); Iter++ )
   {
      HBackupInfo &Backup = Iter->second;
      if ( ObjType == boJobLog && !Backup.LogFlag )
      {
         continue;
      }
      if ( Backup.EnableFlag && !Backup.PausedFlag )
      {
         if ( !SendBackupFile(Syslog, ShrDir, LogDir, SimbatBase, Backup.Addr.c_str(), Backup.Port,
                 ObjType, ObjName, 0, FileExist(FileName) ? BkFileSize(FileName) : -1) )
         {
            BkPrintError("SendBackupFile fail");
            return 8;
         }
      }
   }

   return 0;
}
